//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
// Target Devices:
// Tool versions:
//
// Create Date:    2011-08-18 15:28
// Project Name:
// Description:
//      1.write port can write GUI data to ddr and some config register in fpga
//      2.read port can read back 240X320 picture stored in BRAM and some status register in fpga.
//
// Dependencies:
//
// Revision: 1.0
// Revision 0.01 - File Created
//
//
//////////////////////////////////////////////////////////////////////////////////
`timescale 1ns/1ns
module arm_ebi
#(
    parameter ADDR_WIDTH  = 26,
    parameter DATA_WIDTH  = 16
)
(
    input                       clk,
    input                       rst,

    // ebi interface
    inout [DATA_WIDTH-1:0]      ebi_d,
    input [ADDR_WIDTH-1:0]      ebi_a,
    input                       ebi_ncs,
    input                       ebi_nrd,
    input                       ebi_nwe,

    // control and status reg
    output reg [DATA_WIDTH-1:0] disp_mode,
    output reg [DATA_WIDTH-1:0] offset_h,
    output reg [DATA_WIDTH-1:0] offset_v,
    output reg [DATA_WIDTH-1:0] gui_sel,
    input [DATA_WIDTH-1:0]      edge_pos,
    
    //ddr write
    output                      c1_cmd_clk,
    output reg                  c1_cmd_en,
    output [2:0]                c1_cmd_instr,
    output [5:0]                c1_cmd_bl,
    output [29:0]               c1_cmd_byte_addr,
    input                       c1_cmd_full,

    output                      c1_wr_clk,
    output                      c1_wr_en,
    output [3:0]                c1_wr_mask,
    output [31:0]               c1_wr_data,
    input                       c1_wr_full,
    
     //Frame buffer write port
    input                       buffer_x_clk,
    input                       buffer_x_wr,
    input [16:0]                buffer_x_addr,
    input [7:0]                 buffer_x_d,
    input                       buffer_y_clk,
    input                       buffer_y_wr,
    input [16:0]                buffer_y_addr,
    input [7:0]                 buffer_y_d
);

/********************************************************\
Parameter
\********************************************************/
localparam U_DLY        = 1;
localparam BRAM_X_BASE  = 0;
localparam BRAM_Y_BASE  = 26'h1_2C00; // 320x240
localparam GUI_0_BASE   = 26'h2_5800;
localparam REG_BASE_H   = {(ADDR_WIDTH-5){1'b1}};
localparam DDR_WR       = 3'b000;

/********************************************************\
Signals
\********************************************************/

reg                         ebi_ncs_reg1;
reg                         ebi_ncs_reg2;

reg                         ebi_nrd_reg1;
reg                         ebi_nrd_reg2;

reg                         ebi_nwe_reg1;
reg                         ebi_nwe_reg2;

reg [ADDR_WIDTH-1:0]        ebi_a_reg1;
reg [ADDR_WIDTH-1:0]        ebi_a_reg2;

reg [DATA_WIDTH-1:0]        ebi_d_reg1;
reg [DATA_WIDTH-1:0]        ebi_d_reg2;

reg [DATA_WIDTH-1:0]        ebi_data_reg;
reg                         ebi_out_en;

wire                        wr_en;
reg                         wr_en_reg;
wire                        wr_en_risng;

reg                         bram_sel;       // 0 bram x, 1 bram y
wire [ADDR_WIDTH-1:0]       rd_addr_byte;

wire                        ddr_wr_valid;
reg [DATA_WIDTH-1:0]        ddr_wr_err_cnt;
wire                        ddr_fifo_full;

reg                         data_fifo_wr;
wire                        data_fifo_full;
wire                        data_fifo_alempty;
wire                        data_fifo_empty;
wire [31:0]                 data_fifo_rddata;
reg [15:0]                  data_fifo_wrdata;

reg                         addr_fifo_wr;
wire [31:0]                 addr_fifo_wrdata;
wire                        addr_fifo_full;
wire                        addr_fifo_alempty;
wire                        addr_fifo_empty;
wire [31:0]                 addr_fifo_rddata;

reg                         addr_wr_flag;
reg                         fifo_rd_req;
reg                         fifo_rd_req_dly;
reg                         fifo_rd_req_dly2;
wire                        fifo_alempty;
wire                        fifo_empty;

wire[DATA_WIDTH-1:0]        rd_data_x;
wire[DATA_WIDTH-1:0]        rd_data_y;
reg [15:0]                  rd_addr;

wire                        fpga_reg_valid;

/********************************************************\
main code
\********************************************************/

assign ebi_d = (ebi_out_en==1'b1)?ebi_data_reg:{DATA_WIDTH{1'bZ}};
assign rd_en = ~ebi_ncs_reg2 & (~ebi_nrd_reg2);
assign wr_en = ~ebi_ncs_reg2 & (~ebi_nwe_reg2);
assign wr_en_falling = wr_en_reg & (~wr_en);
assign fpga_reg_valid = (ebi_a_reg2[ADDR_WIDTH-1:5]==REG_BASE_H);
assign rd_addr_byte = (bram_sel==1'b1)?(ebi_a_reg2 - BRAM_Y_BASE):ebi_a_reg2;

assign c1_cmd_clk   = clk;
assign c1_cmd_instr = 3'b000;//DDR_WR;
assign c1_cmd_bl    = 'h0;
//assign c1_cmd_en    = fifo_rd_req_dly2;
assign c1_cmd_byte_addr = addr_fifo_rddata[29:0];

assign c1_wr_clk    = clk;
assign c1_wr_mask   = 4'b0000;
assign c1_wr_en     = fifo_rd_req_dly & (~c1_wr_full);
assign c1_wr_data   = {data_fifo_rddata[15:0],data_fifo_rddata[31:16]};
assign ddr_wr_valid = (ebi_a_reg2 >= GUI_0_BASE) & (~fpga_reg_valid);
assign ddr_fifo_full = c1_cmd_full | c1_wr_full;
assign addr_fifo_wrdata = {{(32-ADDR_WIDTH){1'b0}},ebi_a_reg2[ADDR_WIDTH-1:2],2'h0};
assign fifo_alempty = addr_fifo_alempty | data_fifo_alempty;
assign fifo_empty = addr_fifo_empty | data_fifo_empty;

/**********************  Input signal pipline registe ***************************/

// low active signal, reset to logic 1
always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        ebi_ncs_reg1    <= 1'b1;
        ebi_ncs_reg2    <= 1'b1;
        ebi_nrd_reg1    <= 1'b1;
        ebi_nrd_reg2    <= 1'b1;
        ebi_nwe_reg1    <= 1'b1;
        ebi_nwe_reg2    <= 1'b1;
    end
    else
    begin
        ebi_ncs_reg1    <= #U_DLY ebi_ncs;
        ebi_ncs_reg2    <= #U_DLY ebi_ncs_reg1;
        ebi_nrd_reg1    <= #U_DLY ebi_nrd;
        ebi_nrd_reg2    <= #U_DLY ebi_nrd_reg1;
        ebi_nwe_reg1    <= #U_DLY ebi_nwe;
        ebi_nwe_reg2    <= #U_DLY ebi_nwe_reg1;
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        ebi_a_reg1  <= 'h0;
        ebi_a_reg2  <= 'h0;
        ebi_d_reg1  <= 'h0;
        ebi_d_reg2  <= 'h0;
    end
    else
    begin
        ebi_a_reg1  <= #U_DLY ebi_a;
        ebi_a_reg2  <= #U_DLY ebi_a_reg1;
        ebi_d_reg1  <= #U_DLY ebi_d;
        ebi_d_reg2  <= #U_DLY ebi_d_reg1;
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        ebi_out_en    <= 1'b0;
    end
    else if(rd_en)
    begin
        ebi_out_en    <= #U_DLY 1'b1;
    end
    else if(ebi_ncs_reg2)
    begin
        ebi_out_en    <= #U_DLY 1'b0;
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        wr_en_reg    <= 1'b0;
    end
    else
    begin
        wr_en_reg    <= #U_DLY wr_en;
    end
end

/**********************  FPGA register R/W  ***************************/

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        disp_mode   <= 'h0;
        offset_h    <= 'h0;
        offset_v    <= 'h0;
        gui_sel     <= 'h0;
    end
    else if(fpga_reg_valid & wr_en_falling)
    begin
        case(ebi_a_reg2[4:1])   // total 16 x 16bit register
            'd0: disp_mode  <= #U_DLY ebi_d_reg2;   //0
            'd1: offset_h   <= #U_DLY ebi_d_reg2;   //2
            'd2: offset_v   <= #U_DLY ebi_d_reg2;   //4
            'd3: gui_sel    <= #U_DLY ebi_d_reg2;   //6
            default: ;
        endcase
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        bram_sel    <= 1'b0;
    end
    else
    begin
        bram_sel    <= #U_DLY (ebi_a_reg2 >= BRAM_Y_BASE) & (~fpga_reg_valid);
    end
end

// BRAM read data port is 16bit wide,so rd_addr is word address.
always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        rd_addr <= 'h0;
    end
    else if(rd_en)
    begin
        rd_addr <= #U_DLY rd_addr_byte[16:1];
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        ebi_data_reg    <= 'h0;
    end
    else if(fpga_reg_valid)
    begin
        case(ebi_a_reg2[4:1])   // total 16 x 16bit register
            'd0: ebi_data_reg <= #U_DLY disp_mode;    //0
            'd1: ebi_data_reg <= #U_DLY offset_h;     //2
            'd2: ebi_data_reg <= #U_DLY offset_v;     //4
            'd3: ebi_data_reg <= #U_DLY gui_sel;      //6
            'd4: ebi_data_reg <= #U_DLY edge_pos;
            /*
            'd5: ebi_data_reg <= #U_DLY curr_speed;
            'd6: ebi_data_reg <= #U_DLY {31'h0,sample_done};
            'd7: ebi_data_reg <= #U_DLY phase_speed;
            */
            default:;// ebi_data_reg <= #U_DLY 'h0;
        endcase
    end
    else
    begin
        ebi_data_reg    <= #U_DLY (bram_sel==1'b1)?rd_data_y:rd_data_x;
    end
end

/**********************  DDR write GUI  ***************************/

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        data_fifo_wr    <= 1'b0;
    end
    else if(wr_en_falling & ddr_wr_valid)
    begin
        data_fifo_wr  <= #U_DLY 1'b1;
    end
    else
    begin
        data_fifo_wr  <= #U_DLY 1'b0;
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        addr_wr_flag    <= 1'b0;
    end
    else if(data_fifo_wr)
    begin
        addr_wr_flag  <= #U_DLY ~addr_wr_flag;
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        addr_fifo_wr    <= 1'b0;
    end
    else if(wr_en_falling & addr_wr_flag & ddr_wr_valid)
    begin
        addr_fifo_wr  <= #U_DLY 1'b1;
    end
    else
    begin
        addr_fifo_wr  <= #U_DLY 1'b0;
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        fifo_rd_req    <= 1'b0;
    end
    else if(~fifo_rd_req & (~ddr_fifo_full))
    begin
        fifo_rd_req  <= #U_DLY ~fifo_empty;
    end
    else
    begin
        fifo_rd_req  <= #U_DLY 1'b0;
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        fifo_rd_req_dly     <= 1'b0;
        fifo_rd_req_dly2    <= 1'b0;
    end
    else
    begin
        fifo_rd_req_dly     <= #U_DLY fifo_rd_req;
        fifo_rd_req_dly2    <= #U_DLY fifo_rd_req_dly;
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        c1_cmd_en   <= 1'b0;
    end
    else if(c1_wr_en)
    begin
        c1_cmd_en   <= #U_DLY 1'b1;
    end 
    else if(~c1_cmd_full)
    begin
        c1_cmd_en   <= #U_DLY 1'b0;
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        data_fifo_wrdata    <= 'h0;
    end
    else if(wr_en_falling & ddr_wr_valid)
    begin
        data_fifo_wrdata  <= #U_DLY ebi_d_reg2;
    end
end

always@(posedge clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        ddr_wr_err_cnt  <= 'h0;
    end
    else if((data_fifo_wr & data_fifo_full)|(addr_fifo_wr & addr_fifo_full))
    begin
        ddr_wr_err_cnt  <= #U_DLY ddr_wr_err_cnt + 1'b1;
    end
end

FIFO_16x1024_B DATA_FIFO(
  .rst          (rst),
  .wr_clk       (clk),
  .rd_clk       (clk),
  .din          (data_fifo_wrdata),
  .wr_en        (data_fifo_wr),
  .rd_en        (fifo_rd_req),
  .dout         (data_fifo_rddata),
  .full         (data_fifo_full),
  .almost_full  (),
  .empty        (data_fifo_empty),
  .almost_empty (data_fifo_alempty)
);

FIFO_32x512_B ADDR_FIFO(
  .clk          (clk),
  .rst          (rst),
  .din          (addr_fifo_wrdata),
  .wr_en        (addr_fifo_wr),
  .rd_en        (fifo_rd_req_dly),
  .dout         (addr_fifo_rddata),
  .full         (addr_fifo_full),
  .almost_full  (),
  .empty        (addr_fifo_empty),
  .almost_empty (addr_fifo_alempty)
);

/**********************  FRAME Buffer  ***************************/
BRAM_320X240B FRAME_X(
    .clka           (buffer_x_clk),
    .wea            (buffer_x_wr),
    .addra          (buffer_x_addr),
    .dina           (buffer_x_d),
    .clkb           (clk),
    //.enb            (rd_en_x),
    .addrb          (rd_addr),
    .doutb          (rd_data_x)
);

BRAM_320X240B FRAME_Y(
    .clka           (buffer_y_clk),
    .wea            (buffer_y_wr),
    .addra          (buffer_y_addr),
    .dina           (buffer_y_d),
    .clkb           (clk),
    //.enb            (rd_en_y),
    .addrb          (rd_addr),
    .doutb          (rd_data_y)
);
endmodule
